using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Formula
{
    public class AllCallProvider : CallProviderBase
    {
        private readonly Type _requiredAttribute;
        private List<string> _usingDirectives;
        public List<string> UsingDirectives
        {
            get
            {
                if (_usingDirectives == null)
                    _usingDirectives = new List<string>();
                return _usingDirectives;
            }
        }

        public AllCallProvider() {}

        public AllCallProvider(Type requiredAttribute, params string[] usingDirectives)
        {
            _requiredAttribute = requiredAttribute;
            Using(usingDirectives);
        }

        protected override IEnumerable<Method> GetMethods(Type type)
        {
            IEnumerable<Method> calls = Enumerable.Empty<Method>();
            if (type == null)
            {
                var pos = Method.LastIndexOf('.');
                if (pos >= 0)
                {
                    string typeName = Method.Substring(0, pos);
                    calls = calls.Union(GetStaticMembers(typeName, UsingDirectives));
                }
            }
            else
            {
                calls = (from p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                         select (Method)p)
                    .Union(
                    from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
                    select (Method)m);
            }

            if (_requiredAttribute != null)
                calls = calls.Where(m => m.MethodInfo.GetCustomAttributes(_requiredAttribute, false).Count() > 0);

            return calls;
        }

        private static IEnumerable<Method> GetStaticMembers(string typeName, IEnumerable<string> usingDirectives)
        {
            IEnumerable<Method> calls = null;
            foreach (var usingDirective in usingDirectives.Union(new[] { "" }))
            {
                var name = string.IsNullOrEmpty(usingDirective) ? typeName : usingDirective + "." + typeName;

                var t = Type.GetType(name, false, true);
                if (t != null)
                {
                    calls = (from m in t.GetMethods(BindingFlags.Static | BindingFlags.Public)
                             select (Method) m).Union(
                        (from m in t.GetProperties(BindingFlags.Static | BindingFlags.Public)
                         select new Method {MethodInfo = m, Name = typeName + "." + m.Name}));
                }
            }

            if (calls == null)
                calls = Enumerable.Empty<Method>();

            return calls;
        }

        public AllCallProvider Using(params string[] @namespaces)
        {
            foreach (var @namespace in @namespaces)
                UsingDirectives.Add(@namespace);
                
            return this;
        }
    }
}